
#include "log.h"
#include "helpers.h"
#include "pin.h"
#include "eventmanager.h"

#include <algorithm>
#include <iterator>
#include <string>
#include <iostream>
#include <functional>

#define DELAY_DETECTION 20

using namespace std;

bool Pin::_isSetup = false;
const int Pin::_validPinsRpiBp[17] = {7,0,2,3,21,22,23,24,25,1,4,5,6,26,27,28,29};
PinMap Pin::_pinMap;
bool Pin::_pinWatcherFlag = false;
/*
   InterruptsMap Pin::interruptsMap;

   void Pin::initInterruptsMap() {

   int i;
   Pin* pinObject;
   for(i=0; i< 17; i++){
   pinObject = Pin::getPin(Pin::validPinsRpiBp[i]);

   if(pinObject != NULL && pinObject->mode == INPUT) {

   auto fct = [pinObject](){ Pin::getPin(pinObject->number)->_updateState();};
   Pin::interruptsMap.insert(std::make_pair([pinObject->number],fct));

   if (wiringPiISR(pinObject->number, INT_EDGE_BOTH, Pin::interruptsMap[pinObject->number]) < 0)
   {
   Log::Warning("Unable to setup ISR for pin "+ Helpers::toString(pinObject->number)) ;
   }
   }
   }
   }*/

PI_THREAD (Pin::_pinWatcherRoutine)
{

    Log::Message("Pin watcher Routine Launched");
    PinMap::iterator it;

    Pin::_pinWatcherFlag = true;
    for(;;) {
        for(it = Pin::_pinMap.begin(); it != _pinMap.end(); it++) {
            if(!Pin::_pinWatcherFlag)
                return 0;

            if(it->second->mode == INPUT) 
                it->second->_updateState();
        }

        delay(DELAY_DETECTION);
    }

    return 0;
}


//
//Static function to set up GPIO of the RPI if not
//
void Pin::_initGPIO(){
    //initializing the wiringPi lib once
    if(!Pin::_isSetup) {
        Pin::_isSetup = true;
        wiringPiSetup();
    }
}

Pin* Pin::getPin(int pin){

    PinMap::iterator it;

    it = Pin::_pinMap.find(pin);

    //If the eventIdentifier is not already registered
    if(it == Pin::_pinMap.end()){
        return NULL ;
    }else {
        return it->second;
    }
}

Pin*  Pin::declarePinUse(int pin, int mode) {

    Pin* p = Pin::getPin(pin);
    cout << "check";

    if(p != NULL) {
        if(p->mode != mode) {
            Log::Warning("Invalid use mode for Pin "+Helpers::toString(pin));
            return NULL;
        } else
            return p;

    } else {
        if(_isPinValid(pin)) {
            Pin* newPin = new Pin(pin, mode);
            Pin::_pinMap[pin] = newPin;

            return newPin;

        } else
            return NULL;
    }        

    return NULL;
}

void Pin::startPinWatcher(){
    piThreadCreate(Pin::_pinWatcherRoutine);
}

void Pin::stopPinWatcher(){
    Pin::_pinWatcherFlag = false;
}

int Pin::getRotationDirection(Pin* p1, Pin* p2) {
    if( p1->getState() == p2->getPrevState()) {
        return 1;
    } else if( p1->getState() != p2->getPrevState()) {
        return -1;
    } else {
        return 0;
    }
}




//
//Pin class constructor
//
Pin::Pin(int number, int mode) {

    this->number = number;
    this->mode = mode;

    //init the wiringPi Lib
    Pin::_initGPIO();

    //Set state change hooks
    this->declareEvent("stateChanged");

    //set state depending on the mode
    if(this->mode == OUTPUT) {
        digitalWrite(this->number, LOW);
        this->state = LOW;
    } else {
        this->state =  digitalRead(this->number);
        Log::Message("Initializing interrupts for pin "+Helpers::toString(this->number));
    }
}

//
//Setter for the state: change the state of the pin
//Enabled only if the mode is output
//
void Pin::setState(int state){
    if(this->mode == OUTPUT && this->state != state){
        digitalWrite(this->number, state);
        this->state = state;
        this->fireEvent("stateChanged",new EventHandlerData(this, "State has changed to " + this->_getState()));
    }
}

//
//Check if the targeted pin is a valid pin
//
bool Pin::_isPinValid(int pinNum) {
    unsigned int i=0;
    while(i < sizeof(Pin::_validPinsRpiBp)){
        if(Pin::_validPinsRpiBp[i] == pinNum)
            return true;
        i++;
    }

    return false;
}

string Pin::_getState() {
    if(this->state == HIGH)
        return "HIGH";

    return "LOW";
}

int Pin::getState(){
    return this->state;
}

int Pin::getPrevState(){
    return this->prevState;
}

//
//Read the state of the pin 
//
void Pin::_updateState(void) {

    if(this->mode == INPUT) {
        this->prevState = this->state;
        this->state = digitalRead(this->number);

        if(this->state != this->prevState) {
            Log::Message("State of pin "+ Helpers::toString(this->number)+" has changed to "+ this->_getState());
            this->fireEvent("stateChanged",new EventHandlerData(this, "State has changed to " + this->_getState()));

        }
    }
}

void Pin::printPinState() {
    Log::Message("Pin " + Helpers::toString(this->number) + " : " + Helpers::toString(this->state));
}


